#pragma once

#include "Core/Log.h"

/*
 * buffer 就是缓存
 * BufferElement 着色求片段
 * BufferLayout 就是BufferElement的stack栈
 * VertexBuffer 顶点缓存
 * IndexBuffer 步长缓存
 * */
namespace Hazel {
    enum class ShaderDataType {
        None = 0, Float, Float2, Float3, Float4, Mat3, Mat4, Int, Int2, Int3, Int4, Bool
    };
    /**ShaderDataTypeSize：用于计算偏移*/
    static uint32_t ShaderDataTypeSize(ShaderDataType type) {
        switch (type) {
            case ShaderDataType::Float:         return 4;
            case ShaderDataType::Float2:        return 4 * 2;
            case ShaderDataType::Float3:        return 4 * 3;
            case ShaderDataType::Float4:        return 4 * 4;
            case ShaderDataType::Mat3:          return 4 * 3 * 3;
            case ShaderDataType::Mat4:          return 4 * 4 * 4;
            case ShaderDataType::Int:           return 4;
            case ShaderDataType::Int2:          return 4 * 2;
            case ShaderDataType::Int3:          return 4 * 3;
            case ShaderDataType::Int4:          return 4 * 4;
            case ShaderDataType::Bool:          return 1;
        }
        HZ_TRACE("Unknow ShaderDataType!");
        return 0;
    }

    struct BufferElement {
        std::string Name;
        ShaderDataType Type;
        uint32_t  Size;

        uint32_t Offset;
        bool Normallized;
        BufferElement(){}

        BufferElement( ShaderDataType type, const std::string &name,bool normallized = false)
                : Name(name), Type(type), Size(ShaderDataTypeSize(type)), Offset(0), Normallized(normallized) {}

        uint32_t GetComponentCount() const {
            switch (Type) {
                case ShaderDataType::Float:         return 1;
                case ShaderDataType::Float2:        return 2;
                case ShaderDataType::Float3:        return 3;
                case ShaderDataType::Float4:        return 4;
                case ShaderDataType::Mat3:          return 3 * 3;
                case ShaderDataType::Mat4:          return 4 * 4;
                case ShaderDataType::Int:           return 1;
                case ShaderDataType::Int2:          return 2;
                case ShaderDataType::Int3:          return 3;
                case ShaderDataType::Int4:          return 4;
                case ShaderDataType::Bool:          return 1;
            }
            HZ_TRACE("Unknow ShaderDataType!");
            return 0;
        }

    };

    class BufferLayout {
    public:
        BufferLayout() {}
        BufferLayout(const std::initializer_list<BufferElement>& elements):m_Elements(elements) {
            CalculateOffsetsAndStride();
        }

        inline uint32_t GetStride() const {return m_Stride;}
        inline const std::vector<BufferElement>& GetElements() const {return m_Elements;}
        // 只要有begin和end方法就可以遍历迭代这个类
        std::vector<BufferElement>::iterator begin() {return m_Elements.begin();}
        std::vector<BufferElement>::iterator end() {return m_Elements.end();}
        std::vector<BufferElement>::const_iterator begin() const {return m_Elements.begin();}
        std::vector<BufferElement>::const_iterator end() const {return m_Elements.end();}

    private:
        void CalculateOffsetsAndStride() {
            uint32_t offset = 0;
            m_Stride = 0;
            for (auto& element : m_Elements) {
                element.Offset = offset;
                offset += element.Size;
                m_Stride += element.Size;
            }
        }
    private:
        std::vector<BufferElement> m_Elements;
        uint32_t m_Stride = 0;
    };


    // 存放顶点
    class VertexBuffer {
    public:
        virtual ~VertexBuffer() = default;

        virtual void Bind() const = 0;
        virtual void Unbind() const = 0;

        virtual const BufferLayout& GetLayout() const = 0;
        virtual void SetLayout(const BufferLayout& layout) = 0;

        static Ref<VertexBuffer> Create(float* vertices, uint32_t size);
    };
    //存放连接线
    class IndexBuffer {
    public:
        virtual ~IndexBuffer() = default;

        virtual void Bind() const = 0;
        virtual void Unbind() const = 0;
        virtual uint32_t GetCount() const = 0;
        static Ref<IndexBuffer> Create(uint32_t* indices, uint32_t size);
    };
}
